package base_init

import (
	"fmt"
	_ "github.com/mbobakov/grpc-consul-resolver" // 实现服务负载均衡策略必须引入
	"go-likaladi-shop/common/base_config"
	"go-likaladi-shop/common/base_util"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

// GrpcClientLoadBalance 封装了通过 Consul 进行服务发现和负载均衡的 gRPC 客户端连接过程
// consulConfig: Consul 配置，包含了 Consul 的地址和端口信息
// serverName: 目标服务的名称
// grpcClientFun: 用户传入的处理 gRPC 连接的函数，通常是客户端调用的逻辑
func GrpcClientLoadBalance(consulConfig base_config.ConsulConfig, serverName string, grpcClientFun func(clientConn *grpc.ClientConn)) {
	// 尝试连接到指定的 gRPC 服务，连接使用 Consul 服务发现（通过 consul://协议）
	conn, err := grpc.Dial(
		// 使用 Consul 服务发现，格式为 consul://<host>:<port>/<service_name>?wait=<timeout>
		fmt.Sprintf("consul://%s:%s/%s?wait=14s", consulConfig.Host, consulConfig.Port, serverName),
		grpc.WithInsecure(), // 使用不安全的连接方式（不启用 TLS 加密）。仅适用于开发环境，生产环境应使用安全连接
		grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`), // 设置服务负载均衡策略为 "round_robin"，即轮询负载均衡策略
	)

	// 如果连接出错，记录日志并结束程序
	if err != nil {
		// zap.S().Fatalf() 会打印错误信息并调用 os.Exit(1)，导致程序立刻退出。适用于服务初始化过程中出现致命错误的场景
		zap.S().Fatalf("[%s]服务连接失败: %v", serverName, err) // 使用 zap 日志记录连接失败的错误信息，服务名和错误详情
	}

	// 执行函数：创建 gRPC 客户端，调用 gRPC 方法时需要使用该客户端
	grpcClientFun(conn)
}

// GrpcClientCustomPolicy 通过自定义的 Consul 配置连接服务
// consulConfig: Consul 配置，包含了 Consul 的地址和端口信息
// serverName: 目标服务的名称
// grpcClientFun: 用户传入的处理 gRPC 连接的函数，通常是客户端调用的逻辑
func GrpcClientCustomPolicy(consulConfig base_config.ConsulConfig, serverName string, grpcClientFun func(clientConn *grpc.ClientConn)) {
	// 创建一个 Consul 客户端，用于从 Consul 获取服务信息
	client := base_util.CreateConsulClient(consulConfig)

	// 使用 Consul 客户端查询指定服务的信息
	data, err := client.Agent().ServicesWithFilter(fmt.Sprintf(`Service == "%s"`, serverName))
	if err != nil {
		// zap.S().Fatalf() 会打印错误信息并调用 os.Exit(1)，导致程序立刻退出
		zap.S().Fatalf("注册中心查找[%s]信息失败: %v", serverName, err)
	}

	// 获取服务的 IP 地址和端口
	userSrvHost := ""
	userSrvPort := 0
	for _, value := range data {
		userSrvHost = value.Address
		userSrvPort = value.Port
		break
	}

	// 如果没有获取到服务信息，说明服务不可达，直接退出
	if userSrvHost == "" || userSrvPort == 0 {
		// zap.S().Fatalf() 会打印错误信息并调用 os.Exit(1)，导致程序立刻退出
		zap.S().Fatalf("用户服务不可达")
	}

	// 根据从 Consul 获取的 IP 和端口，连接目标服务
	GrpcClientHostPort(userSrvHost, userSrvPort, grpcClientFun)
}

// GrpcClientHostPort 通过指定的主机和端口连接到 gRPC 服务
// host: 服务的 IP 地址
// port: 服务的端口
// grpcClientFun: 用户传入的处理 gRPC 连接的函数，通常是客户端调用的逻辑
func GrpcClientHostPort(host string, port int, grpcClientFun func(clientConn *grpc.ClientConn)) {
	// 通过 gRPC 的 Dial 函数连接服务，使用不安全连接方式
	// grpc.WithInsecure() 选项适用于本地开发环境。生产环境应使用 grpc.WithTransportCredentials() 配置 TLS 加密
	conn, err := grpc.Dial(fmt.Sprintf("%s:%d", host, port), grpc.WithInsecure())
	if err != nil {
		// zap.S().Fatalf() 会打印错误信息并调用 os.Exit(1)，导致程序立刻退出
		zap.S().Fatalf("服务连接异常: %v", err) // 使用 zap 日志记录连接失败的错误信息，服务名和错误详情
	}

	// 执行函数：创建 gRPC 客户端，调用 gRPC 方法时需要使用该客户端
	grpcClientFun(conn)
}
